// Copyright 2009 Pigasus.
// GNU GPL v2.
// Author: John Zhang and Jordan Rupprecht.

#include "serialization.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sstream>
#include <string>

#include "pigasus-types.h"

using namespace std;

string AbbreviateType(string type) {
  return type.substr(0, kTypeAbbreviationLength);
}

string FormatSize(int size) {
  char buffer[15];
  sprintf(buffer, "%010d", size);
  return string(buffer);
}

string GetNextType(string buffer) {
  return buffer.substr(0, kTypeAbbreviationLength);
}

string Serialize(int v)  {
  string buffer;
  stringstream stream;
  stream << v;
  buffer = stream.str();
  return AbbreviateType(kIntType) + FormatSize(buffer.size()) + buffer;
}

int UnserializeInt(string &buffer) {
  string int_str = UnserializeString(buffer);
  int i;
  sscanf(int_str.c_str(), "%d", &i);
  return i;
}

string Serialize(double v) {
  string buffer;
  stringstream stream;
  stream << v;
  buffer = stream.str();
  return AbbreviateType(kFloatType) + FormatSize(buffer.size()) + buffer;
}

double UnserializeFloat(string &buffer) {
  string float_str = UnserializeString(buffer);
  double i;
  sscanf(float_str.c_str(), "%lf", &i);
  return i;
}

string Serialize(string v) {
  return AbbreviateType(kStringType) + FormatSize(v.size()) + v;
}

string UnserializeString(string &buffer) {
  string type = buffer.substr(0, kTypeAbbreviationLength);
  int size = atoi(buffer.substr(kTypeAbbreviationLength,
                                kFormattedSizeLength).c_str());
  string res = buffer.substr(kTypeAbbreviationLength + kFormattedSizeLength,
                             size);

  int removed_length = kTypeAbbreviationLength + kFormattedSizeLength + size;
  
  buffer = buffer.substr(removed_length, buffer.size() - removed_length + 1);
  return res;
}

string Serialize(List &v) {
  string buffer;
  buffer = AbbreviateType(kListType) + FormatSize(v.GetSize());
  for (int i = 0; i < v.GetSize(); i++) {
    if (v.GetType(i) == kIntType) {
      buffer += Serialize(*((int*)v.Get(i)));
    } else if (v.GetType(i) == kFloatType) {
      buffer += Serialize(*((double*)v.Get(i)));
    } else if (v.GetType(i) == kStringType) {
      buffer += Serialize(*((string*)v.Get(i)));
    } else if (v.GetType(i) == kListType) {
      buffer += Serialize(*((List*)v.Get(i)));
    } else if (v.GetType(i) == kMapType) {
      buffer += Serialize(*((Map*)v.Get(i)));
    } else if (v.GetType(i) == kFileType) {
      buffer += Serialize(*((File*)v.Get(i)));
    } else {
      // Do nothing.
    }
  }

  return buffer;
}

List* UnserializeList(string &buffer) {
  string type = buffer.substr(0, kTypeAbbreviationLength);
  int size = atoi(buffer.substr(kTypeAbbreviationLength,
                                kFormattedSizeLength).c_str());
  
  int removed_length = kTypeAbbreviationLength + kFormattedSizeLength;  
  buffer = buffer.substr(removed_length, buffer.size() - removed_length + 1);

  List *res = new List();
  for (int i = 0; i < size; i++) {
    string type = GetNextType(buffer);
    if (type == AbbreviateType(kIntType)) {
      res->Append(UnserializeInt(buffer));
    } else if (type == AbbreviateType(kFloatType)) {
      res->Append(UnserializeFloat(buffer));
    } else if (type == AbbreviateType(kStringType)) {
      res->Append(UnserializeString(buffer));
    } else if (type == AbbreviateType(kListType)) {
      List *uns = (UnserializeList(buffer));
      res->Append(*uns);
      delete uns;
    } else if (type == AbbreviateType(kMapType)) {
      Map *uns = (UnserializeMap(buffer));
      res->Append(*uns);
      delete uns;
    } else if (type == AbbreviateType(kFileType)) {
      File *uns = (UnserializeFile(buffer));
      res->Append(*uns);
      delete uns;
    } else {
      // Do nothing.
    }
  }
  return res;
}

string Serialize(Map &v) {
  string buffer;
  buffer = AbbreviateType(kMapType) + FormatSize(v.GetSize());

  List keys = v.GetKeys();
  for (int i = 0; i < keys.GetSize(); i++) {
    string key = *((string*)keys.Get(i));
    buffer += Serialize(key);

    if (v.GetType(key) == kIntType) {
      buffer += Serialize(*((int*)v.Get(key)));
    } else if (v.GetType(key) == kFloatType) {
      buffer += Serialize(*((double*)v.Get(key)));
    } else if (v.GetType(key) == kStringType) {
      buffer += Serialize(*((string*)v.Get(key)));
    } else if (v.GetType(key) == kListType) {
      buffer += Serialize(*((List*)v.Get(key)));
    } else if (v.GetType(key) == kMapType) {
      buffer += Serialize(*((Map*)v.Get(key)));
    } else if (v.GetType(key) == kFileType) {
      buffer += Serialize(*((File*)v.Get(key)));
    } else {
      // Do nothing.
    }
  }
  return buffer;
}

Map* UnserializeMap(string &buffer) {
  string type = buffer.substr(0, kTypeAbbreviationLength);
  int size = atoi(buffer.substr(kTypeAbbreviationLength,
                                kFormattedSizeLength).c_str());
  
  int removed_length = kTypeAbbreviationLength + kFormattedSizeLength;  
  buffer = buffer.substr(removed_length, buffer.size() - removed_length + 1);

  Map *res = new Map();
  for (int i = 0; i < size; i++) {
    string key = UnserializeString(buffer);
    string type = GetNextType(buffer);
    if (type == AbbreviateType(kIntType)) {
      res->Set(key, UnserializeInt(buffer));
    } else if (type == AbbreviateType(kFloatType)) {
      res->Set(key, UnserializeFloat(buffer));
    } else if (type == AbbreviateType(kStringType)) {
      res->Set(key, UnserializeString(buffer));
    } else if (type == AbbreviateType(kListType)) {
      List *uns = (UnserializeList(buffer));
      res->Set(key, *uns);
      delete uns;
    } else if (type == AbbreviateType(kMapType)) {
      Map *uns = (UnserializeMap(buffer));
      res->Set(key, *uns);
      delete uns;
    } else if (type == AbbreviateType(kFileType)) {
      File *uns = (UnserializeFile(buffer));
      res->Set(key, *uns);
      delete uns;
    } else {
      // Do nothing.
    }
  }
  return res;
}

string Serialize(File &v) {
  return AbbreviateType(kFileType) + FormatSize(v.Filename().size())
    + v.Filename();
}

File* UnserializeFile(string &buffer) {
  string filename = UnserializeString(buffer);
  File *file = new File(filename);
  return file;
}

/*
int main() {
  Map m;
  m.Set("hello", "world");

  List l;
  l.Append(1);
  l.Append("hi");
  l.Append(m);
  l.Append(((File)"bob.txt"));

  string s = Serialize(l);
  WriteStringToFile("test.out", s);
  
  string ss;
  ReadStringFromFile("test.out", ss);

  List *ll = UnserializeList(ss);
  Map *mm = (Map*)ll->Get(2);
  File *ff = (File*)ll->Get(3);

  cout << *((int*)ll->Get(0)) << endl;
  cout << *((string*)ll->Get(1)) << endl;
  cout << *((string*)mm->Get("hello")) << endl;
  cout << ff->Filename() << endl;
  
  delete ll;

  return 0;
}
*/
